package com.corejsf;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import javax.annotation.Resource;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.sql.DataSource;

/**
 * This class stores data on a registered user.
 *
 * @author Victor Lu
 * @author Vukasin Simic
 * @version 1.0
 */

@SessionScoped
@Named("user")
public class User implements Serializable {
    /**
     * serial Version UID.
     */
    private static final long serialVersionUID = -3346017799022042616L;

    /**
     * DataSource for JBoss AS 7.1 connection pool.
     */
    @Resource(mappedName = "java:jboss/datasources/vvdb")
    private DataSource dataSource;

    /**
     * User model.
     */
    @Inject private UserModel um;

    /**
     * Uniquely identifying username.
     */
    private String userName;
    /**
     * User last name.
     */
    private String lastName;
    /**
     * User first name.
     */
    private String firstName;
    /**
     * User password, checked against for authentication.
     */
    private String password;
    /**
     * User Admin authorization.
     */
    private boolean admin;

    /**
     * Uniquely identifying number assigned by UserManger.
     */
    private int employeeNumber;
    /**
     * Indicator of whether User is being edited.
     */
    private boolean editable;

    /**
     * Users list.
     */
    private static ArrayList<User> users = new ArrayList<User>();

    /**
     * Updates user list from database.
     *
     * @return users.
     */
    public ArrayList<User> getAllUsers() {
        if (checkUserList()) {
            Connection connection = null;
            Statement stmt = null;
            try {
                try {
                    connection = dataSource.getConnection();
                    try {
                        stmt = connection.createStatement();
                        ResultSet result = stmt
                                .executeQuery("SELECT * FROM Users");
                        while (result.next()) {
                            users.add(new User(result.getString("username"),
                                    result.getString("lastname"), result
                                            .getString("firstname"), result
                                            .getString("password"), result
                                            .getBoolean("admin"), result
                                            .getInt("employeenumber")));
                        }
                    } finally {
                        if (stmt != null) {
                            stmt.close();
                        }

                    }
                } finally {
                    if (connection != null) {
                        connection.close();
                    }
                }
            } catch (SQLException ex) {
                System.out.println("Error in getAllUsers");
                ex.printStackTrace();
            }
            return users;
        } else {
            return users;
        }
    }

    /**
     * Checks whether user list is empty.
     *
     * @return boolean
     */
    public boolean checkUserList() {
        if (users.isEmpty()){
            return true;
        }
        return false;
    }

    /**
     * Default Constructor.
     */
    public User() {
    }

    /**
     * Main Constructor.
     *
     * @param userName
     *            to be assigned to User record
     * @param lastName
     *            to be assigned to User record
     * @param firstName
     *            to be assigned to User record
     * @param password
     *            to be assigned to User record
     * @param admin
     *            privilege to be assigned to User record
     * @param employeeNumber
     *            to be assigned to User record
     */
    public User(final String userName, final String lastName,
            final String firstName, final String password, final boolean admin,
            final int employeeNumber) {
        this.userName = userName;
        this.lastName = lastName;
        this.firstName = firstName;
        this.password = password;
        this.admin = admin;
        this.employeeNumber = employeeNumber;
    }

    /**
     * Adds user, returns nav outcome string.
     * @return result
     */
    public String addUser() {
        Connection connection = null;
        PreparedStatement stmt = null;
        String result = "failure";
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection
                            .prepareStatement("INSERT INTO Users VALUES "
                            + "(?,?,?,?,?, DEFAULT, 1)");
                    stmt.setString(1, um.getUserName());
                    stmt.setString(2, um.getLastName());
                    stmt.setString(3, um.getFirstName());
                    stmt.setString(4, um.getPassword());
                    stmt.setBoolean(5, um.isAdmin());
                    stmt.executeUpdate();
                    result = "success";

                    users.clear();
                    getAllUsers();
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }
                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in addUser()");
            ex.printStackTrace();
        }
        return result;
    }

    /**
     * Deleting a user using the button in the UI.
     *
     * @param userToDelete
     *            from existing user accounts
     * @return null in lieu of navigation outcome string to prompt refresh
     */
    public String deleteUser(final User userToDelete) {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection.prepareStatement("DELETE FROM Users"
                        + " WHERE Username = ?");
                    stmt.setString(1, userToDelete.getUserName());
                    stmt.executeUpdate();
                    users.remove(userToDelete);
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }
                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in deleteUser()");
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Saves the changes made after editing a user.
     *
     * @return null in lieu of navigation outcome string to prompt refresh
     */
    public String save() {
        for (User user : getAllUsers()) {
            if (user.isEditable()) {
                updateUser(user);
                user.setEditable(false);
            }

        }
        return null;
    }

    /**
     * Updating a user.
     *
     * @param userToUpdate
     *            from existing user accounts
     */
    private void updateUser(final User userToUpdate) {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection.prepareStatement("UPDATE Users SET "
                            + "Username = ?, LastName = ?, FirstName = ?,"
                            + "Password = ?, Admin = ? "
                            + "WHERE EmployeeNumber = ?");
                    stmt.setString(1, userToUpdate.getUserName());
                    stmt.setString(2, userToUpdate.getLastName());
                    stmt.setString(3, userToUpdate.getFirstName());
                    stmt.setString(4, userToUpdate.getPassword());
                    stmt.setBoolean(5, userToUpdate.isAdmin());
                    stmt.setInt(6, userToUpdate.getEmployeeNumber());
                    stmt.executeUpdate();
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }
                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in updateUser()");
            ex.printStackTrace();
        }
    }

    /**
     * Gets user name associated to this User account.
     *
     * @return userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * Sets this User account's user name.
     *
     * @param userName
     *            to apply to this User
     */
    public void setUserName(final String userName) {
        this.userName = userName;
    }

    /**
     * Gets last name associated to this User account.
     *
     * @return lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Sets this User account's last name.
     *
     * @param lastName
     *            to apply to this User
     */
    public void setLastName(final String lastName) {
        this.lastName = lastName;
    }

    /**
     * Gets first name associated to this User account.
     *
     * @return firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets this User account's first name.
     *
     * @param firstName
     *            to apply to this User
     */
    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }

    /**
     * Gets password associated to this User account.
     *
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * Sets this User account's password.
     *
     * @param password
     *            to apply to this User
     */
    public void setPassword(final String password) {
        this.password = password;
    }

    /**
     * Gets Admin permission associated this User account.
     *
     * @return admin
     */
    public boolean isAdmin() {
        return admin;
    }

    /**
     * Sets this User's Admin privileges.
     *
     * @param admin
     *            permission to apply to this User
     */
    public void setAdmin(final boolean admin) {
        this.admin = admin;
    }

    /**
     * Gets employee number associated this User account.
     *
     * @return employeeNumber
     */
    public int getEmployeeNumber() {
        return employeeNumber;
    }

    /**
     * Sets this User's employee number.
     *
     * @param employeeNumber
     *            to apply to this User
     */
    public void setEmployeeNumber(final int employeeNumber) {
        this.employeeNumber = employeeNumber;
    }

    /**
     * Gets current editing state of this User object.
     *
     * @return editable
     */
    public boolean isEditable() {
        return editable;
    }

    /**
     * Sets this User's editing state.
     *
     * @param editable
     *            state of User to change
     */
    public void setEditable(final boolean editable) {
        this.editable = editable;
    }

}
